/*
 * file:  expr.cpp (implementation of Expr).
 * Author: Jeremy Johnson 
 * Date: 2/5/07
 *
 * Description: 
 */

#include <iostream>
#include <string>
#include <map>
#include <list>
#include <cstdlib>
#include "programext.h"

using namespace std;


void Sequence::insertListElement(Expr *listElement)
{
	listElements.push_back(listElement);
}
void Sequence::print()
{
	int size = listElements.size();
	for(int i=size-1;i>=0;i--)
	{
		listElements[i]->print();
		if(i!=0)
			std::cout<<",";
	}
}

List::List()
{
	sequence = NULL;
}

List::~List()
{
	delete sequence;
}
void List::insertSequence(Sequence *seq)
{
	sequence = seq;
}
void List::print()
{
	std::cout<<"[";
	if(sequence!=NULL)
		sequence->print();
	std::cout<<"]";
}

int List::getListSize()
{
	return sequence->listElements.size();
}

Element List::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return Element(this);
}

void Number::print()
{
	std::cout<<value_;
}
int Number::value()
{
	return value_;
}
//////////element functions
Element::Element(int _val)
{
	val = _val;
	
	isList = false;
}

Element::Element(List *_lst)
{
	lst = _lst;
	
	isList = true;
}

int Element::value()
{//TO DO: need to make a check if element is an integer
	return val;
}

void Element::print()
{
	if(isList)
		lst->print();
	else
		cout<<val;
}

Element& Element::operator=(const Element& elm)
{
	lst = elm.lst;
	val = elm.val;
	isList = elm.isList;
	
	return *this;
}

//////////////////////////
Element Concatenate::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	List *lst1, *lst2;
	lst1 = l1->eval(NT,FT).getList();
	lst2 = l2->eval(NT,FT).getList();
	
	List *lst = new List();
	lst->insertSequence(lst2->getSequence());
	
	for(int i=0;i<lst1->getListSize();i++)
	{
		lst->getSequence()->insertListElement(lst1->sequence->listElements[i]);
	}

	return Element(lst);
}

Element Cons::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	List *_list, *_list2;

	_list = list->eval(NT,FT).getList();
	
	List *lst = new List();
	lst->insertSequence(_list->getSequence());
	
	if(expr->eval(NT,FT).isList)
	{
		_list2 = expr->eval(NT,FT).getList();
		for(int i=0;i<_list->getListSize();i++)
		{
			lst->getSequence()->insertListElement(_list2->sequence->listElements[i]);
		}
	}
	else
	{
		lst->getSequence()->insertListElement(expr);
	}

	return Element(lst);
}

Element Cdr::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	List *list;

	list = expr->eval(NT,FT).getList();
	
	List *result = new List();
	Sequence *sequencePtr = new Sequence(); 
	for(int i=0;i<list->getListSize()-1;i++)
	{
		sequencePtr->insertListElement(list->sequence->listElements[i]);
		
	}
	result->insertSequence(sequencePtr);

	return Element(result);
}

Element Car::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	List *list;

	list = expr->eval(NT,FT).getList();
	
	List *result = new List();
	Sequence *sequencePtr = new Sequence(); 
	sequencePtr->insertListElement(list->sequence->listElements[list->getListSize()-1]);
	result->insertSequence(sequencePtr);

	return Element(result);
}

Element Nullp::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	List *lst1;
	lst1 = lst->eval(NT,FT).getList();
	
	if(lst1->getListSize() > 0)
		return Element(0);
	else
		return Element(1);
}

Element Intp::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	if(!exp->isList())
		return Element(1);
	else
		return Element(0);
}
Element Listp::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	if(exp->isList())
		return Element(1);
	else
		return Element(0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Program::Program(StmtList *SL)
{
NameTable_.clear();
FunctionTable_.clear();
SL_ = SL;
}

void Program::dump() 
{
  map<string,Element>::iterator p;
  map<string,Proc*>::iterator f;

  cout << "Dump of Symbol Table" << endl;
  cout << "Name Table" << endl;
  for (p = NameTable_.begin();p != NameTable_.end();p++)
  {
    cout << p->first << " -> " ; p->second.print();cout << endl;
  }
  cout << "Function Table" << endl;
  for (f = FunctionTable_.begin();f != FunctionTable_.end();f++) 
    cout << f->first << endl;
}

void Program::eval() 
{
	SL_->eval(NameTable_, FunctionTable_);
}


void StmtList::insert(Stmt * S)
{
  SL_.push_front(S);
}

void StmtList::eval(map<string,Element> &NT, map<string,Proc*> &FT) 
{
  list<Stmt*>::iterator Sp;
  for (Sp = SL_.begin();Sp != SL_.end();Sp++)
	(*Sp)->eval(NT,FT);
}

AssignStmt::AssignStmt(string name, Expr *E)
{
  name_ = name;
  E_ = E;
}

void AssignStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT) const
{
	NT[name_] = E_->eval(NT,FT);
}

DefineStmt::~DefineStmt()
{
  delete P_; 
}

DefineStmt::DefineStmt(string name, Proc *P)
{
  name_ = name;
  P_ = P;
}

void DefineStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT) const
{
	FT[name_] = P_;
}

IfStmt::IfStmt(Expr *E, StmtList *S1, StmtList *S2)
{
  E_ = E;
  S1_ = S1;
  S2_ = S2;
}

IfStmt::~IfStmt() { delete E_; delete S1_; delete S2_; }

void IfStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT) const
{
	if (E_->eval(NT,FT).value() > 0)
		S1_->eval(NT,FT);
	else
		S2_->eval(NT,FT);
}

WhileStmt::WhileStmt(Expr *E, StmtList *S)
{
  E_ = E;
  S_ = S;
}

WhileStmt::~WhileStmt() { delete E_; delete S_; }

void WhileStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT) const
{
	while (E_->eval(NT,FT).value() > 0) 
		S_->eval(NT,FT);
}

Number::Number(int value)
{
	value_ = value;
}

Element Number::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return Element(value_);
}

Ident::Ident(string name)
{
	name_ = name;
}

Element Ident::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return NT[name_];
}

Plus::Plus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Plus::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return Element(op1_->eval(NT,FT).value() + op2_->eval(NT,FT).value());
}

Minus::Minus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Minus::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return Element(op1_->eval(NT,FT).value() - op2_->eval(NT,FT).value());
}

Times::Times(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Times::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return Element(op1_->eval(NT,FT).value() * op2_->eval(NT,FT).value());
}

FunCall::FunCall(string name, list<Expr*> *AL)
{
	name_= name;
	AL_ = AL;
}

FunCall::~FunCall() { delete AL_; }

Element FunCall::eval(map<string,Element> NT, map<string,Proc*> FT)
{
	return FT[name_]->apply(NT, FT, AL_);
}


Proc::Proc(list<string> *PL, StmtList *SL)
{
	SL_ = SL;
	PL_ = PL;
	NumParam_ = PL->size();
}

Element Proc::apply(map<string,Element> &NT, map<string,Proc*> &FT, list<Expr*> *EL) 
{
	map<string,Element> NNT;
	NNT.clear();

	// bind parameters in new name table

	list<string>::iterator p;
	list<Expr*>::iterator e;
	if (NumParam_ != EL->size()) {
		cout << "Param count does not match" << endl;
		exit(1);
	}
	for (p = PL_->begin(), e = EL->begin(); p != PL_->end(); p++, e++) 
		NNT[*p] = (*e)->eval(NT,FT);

	// evaluate function body using new name table and old function table

	SL_->eval(NNT,FT);
	if ( NNT.find("return") != NNT.end() )
		return NNT["return"];
	else {
		cout << "Error:  no return value" << endl;
		exit(1);
	}
}
